/*
 * This file is part of the Refran Library
 * Copyright (C) 2009 César Enrique García
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/** @file
 * $Author: cquike $
 * $Date: 2010-10-11 11:26:54 +0000 (Mon, 11 Oct 2010) $
 * $Revision: 227 $
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef REFRAN_RECIPE_H
#define REFRAN_RECIPE_H

#include <mutex>
#include <boost/noncopyable.hpp>
#include "Logger.h"
#include "RecipeState.h"
#include "FrameSet.h"

namespace Refran
{

    /**
     * The basic Recipe class.
     */
    class Recipe : public Logger, private boost::noncopyable
    {
        public:

        /**
         * Constructor from the Recipe definition
         */
        Recipe(int                version,
               const std::string& name,
               const std::string& synopsis = "",
               const std::string& longDesc = "",
               const std::string& author = "",
               const std::string& email = "",
               const std::string& copyright = "",
               const std::string& url = "",
               const std::string& releaseComment = "");


        /**
         * Destructor
         */
        virtual ~Recipe();

        /**
         * The interface to run the recipe
         */
        void go();

        /**
         * Get state
         */
        RecipeState getState() const;

        /**
         *  Get the tags of input frames that this recipe would allow
         */
        std::list<std::string> getAcceptedInputTags() const;

        /**
         *  Get the input set of frames
         */
        void setInputFrames(std::shared_ptr<FrameSet>& inputFrameSet);

        /**
         *  Get the input set of frames
         */
        const FrameSet& getInputFrames() const;

        /**
         *  Get the output set of frames
         */
        const FrameSet& getOutputFrames() const;

        /**
         *  Get the parameter list for this recipe
         */
        //virtual void getParameters();

        ///Accessor to the version
        int getVersion() const;

        ///Accessor to the name
        const std::string& getName() const;

        ///Accessor to the synopsis
        const std::string& getSynopsis() const;

        ///Accessor to the description
        const std::string& getDescription() const;

        ///Accessor to the author
        const std::string& getAuthor() const;

        ///Accessor to the author email
        const std::string& getEmail() const;

        ///Accessor to the copyright
        const std::string& getCopyright() const;

        ///Accessor to the url
        const std::string& getURL() const;

        ///Accessor to the release comment
        const std::string& getReleaseComment() const;

        ///Get the fraction of the work done
        double getProgress() const;

        protected:

        /**
         * Recipe initialization
         */
        void init_(int                version,
                   const std::string& name,
                   const std::string& synopsis = "",
                   const std::string& longDesc = "",
                   const std::string& author = "",
                   const std::string& email = "",
                   const std::string& copyright = "",
                   const std::string& url = "",
                   const std::string& releaseComment = "");
    
        /**
         * The method which actually implements the recipe.
         */
        virtual void go_impl_() = 0;

        /**
         *  Set the input set of frames
         */
        void setAcceptedInputTags_(const std::list<std::string>& tags) const;

        /**
         *  Set the output set of frames
         */
        void setOutputFrames_(const FrameSet& outputFrames) const;

        /**
         * Set the percentage of the work done. If has to be in the
         * range [0,1], otherwise it is set to 0 or 1.
         * If the value is less than the current one, the current value is kept
         */
        void setProgress_(double fractionDone);

        private:

        /**
         * Default Constructor. Made private, the public constructor has
         * to be used.
         */
        Recipe();

        /**
         * Things we have to do before running the recipe
         */
        void prepareGo_();

        /**
         * Things we have to do after running the recipe
         */
        void finishGo_();

        /**
         * Set the recipe state
         */
        void setState_(const RecipeState& state);

        /**
         * Check that the input frames are compatible with the expected ones
         */
        //bool checkExpectedTags_();

        /**
         * Check that the input frames exist
         */
        //bool checkInputsExist_();

        /**
         * The input frames upon this Recipe will work
         */
        std::shared_ptr<FrameSet> m_InputFrames;

        /**
         * The products of this Recipe
         */
        std::shared_ptr<FrameSet> m_OutputFrames;

        /**
         * The input frames that this recipe requires
         */
        std::list<std::string> m_acceptedInputTags;

        /**
         * The state of the recipe
         */
        RecipeState m_state;

        /**
         * The version of the recipe
         */
        int m_version;

        /**
         * The name of the recipe
         */
        std::string m_name;

        /**
         * The synopsis of the recipe
         */
        std::string m_synopsis;

        /**
         * The description of the recipe
         */
        std::string m_longDesc;

        /**
         * The author  of the recipe
         */
        std::string m_author;

        /**
         * The email of the author of the recipe
         */
        std::string m_email;

        /**
         * The copyright statment of the recipe
         */
        std::string m_copyright;

        /**
         * The URL where to get the recipe from
         */
        std::string m_url;

        /**
         * A specific comment for this release (for vendor specific info)
         */
        std::string m_releaseComment;

        /**
         * The fraction of the work that has been done. In the range [0,1]
         */
        double m_fractionDone;

        /**
         * Mutex for the Recipe State
         * Maybe a boost::shared_mutex can be used, which would allow to have many clients 
         * reading the state without blocking.
         * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mutex_synop
         */
        mutable std::mutex m_state_mutex;


};

}// namespace Refran

#endif // REFRAN_RECIPE_H
